﻿#region Using Statements
using System;
using Microsoft.Xna.Framework;
#endregion

namespace StarFox.ScreenComponents
{
    /// <summary>
    /// Implementation of the player starship.
    /// <para>Definition of movement and collision related members.</para>
    /// </summary>
    partial class StarShip
    {
        #region Fields

        StarShipRotation rotCtrl;
        StarShipTranslation posCtrl;
        StarShipVelocity velCtrl;

        Matrix rotMatrix = Matrix.Identity;
        Matrix transMatrix = Matrix.CreateTranslation(Vector3.Zero);

        BoundingSphere centerSphere = new BoundingSphere() { Radius = 1.5f };
        BoundingSphere leftSphere = new BoundingSphere() { Radius = 1.5f };
        BoundingSphere rightSphere = new BoundingSphere() { Radius = 1.5f };
        BoundingSphere globalSphere = new BoundingSphere() { Radius = 75.0f };

        #endregion

        #region Properties

        /// <summary>
        /// Gets the StarShip's center bounding sphere.
        /// </summary>
        public BoundingSphere CenterSphere { get { return centerSphere; } }

        /// <summary>
        /// Gets the StarShip's left bounding sphere.
        /// </summary>
        public BoundingSphere LeftSphere { get { return leftSphere; } }

        /// <summary>
        /// Gets the StarShip's right bounding sphere.
        /// </summary>
        public BoundingSphere RightSphere { get { return rightSphere; } }

        /// <summary>
        /// Gets the StarShip's global bounding sphere.
        /// </summary>
        public BoundingSphere GlobalSphere { get { return globalSphere; } }

        /// <summary>
        /// Gets the StarShip's position.
        /// </summary>
        public Vector3 Position { get { return Camera.Position; } }

        /// <summary>
        /// Gets the StarShip's velocity (0.0 -> minimum velocity - 1.0 -> maximum velocity).
        /// </summary>
        public float Velocity
        {
            get;
            private set;
        }

        #endregion

        #region Collisions

        /// <summary>
        /// Called when the StarShip collides with an asteroid.
        /// </summary>
        /// <param name="type">The type of the collision.</param>
        public void Collision(AsteroidField.CollisionType? type)
        {
            switch (type)
            {
                case AsteroidField.CollisionType.LEFT:
                    {
                        Shake(10.0f, TimeSpan.FromMilliseconds(1000));
                        Health -= StarFoxHelper.RandomFloat(0.05f, 0.15f);
                        Health = MathHelper.Clamp(Health, 0.0f, 1.0f);
                        break;
                    }
                case AsteroidField.CollisionType.CENTER:
                    {
                        Shake(20.0f, TimeSpan.FromMilliseconds(1000));
                        Health -= StarFoxHelper.RandomFloat(0.05f, 0.15f);
                        Health = MathHelper.Clamp(Health, 0.0f, 1.0f);
                        break;
                    }
                case AsteroidField.CollisionType.RIGHT:
                    {
                        Shake(10.0f, TimeSpan.FromMilliseconds(1000));
                        Health -= StarFoxHelper.RandomFloat(0.05f, 0.15f);
                        Health = MathHelper.Clamp(Health, 0.0f, 1.0f);
                        break;
                    }
                default:
                    {
                        Shake(5.0f, TimeSpan.FromMilliseconds(1000));
                        break;
                    }
            }
        }

        #endregion

        #region PowerUp

        /// <summary>
        /// Called when the StarShip gets a power-up.
        /// </summary>
        /// <param name="type">The type of the power-up.</param>
        public void PowerUp(PowerUp.PowerUpType type)
        {
            switch (type)
            {
                case ScreenComponents.PowerUp.PowerUpType.BOMB:
                    {
                        // only five bombs
                        if (Bombs < 5)
                            Bombs++;

                        break;
                    }
                case ScreenComponents.PowerUp.PowerUpType.FUEL:
                    {
                        velCtrl.IncreaseFuel();
                        break;
                    }
                case ScreenComponents.PowerUp.PowerUpType.HEALTH:
                    {
                        Health += 0.10f;
                        Health = MathHelper.Clamp(Health, 0.0f, 1.0f);
                        break;
                    }
                case ScreenComponents.PowerUp.PowerUpType.BOOST:
                    {
                        velCtrl.IncreaseBoost();
                        break;
                    }
                default:
                    break;
            }
        }

        #endregion

        #region Shake

        /// <summary>
        /// Causes the StarShip to shake.
        /// </summary>
        /// <param name="magnitude">The magnitude of the shake.</param>
        /// <param name="duration">The duration of the shake.</param>
        private void Shake(float magnitude, TimeSpan duration)
        {
            posCtrl.Shake(magnitude, duration);
            rotCtrl.Shake(magnitude, duration);
        }

        #endregion

        #region Start, Stop

        /// <summary>
        /// Causes the StarShip to start moving.
        /// </summary>
        public void Start()
        {
            posCtrl.Start();
            velCtrl.Start();
        }

        /// <summary>
        /// Causes the StarShip to stop moving.
        /// </summary>
        public void Stop()
        {
            posCtrl.Stop();
            velCtrl.Stop();
        }

        #endregion

        #region Initialize_Physics

        private void Initialize_Physics()
        {
            // start motor
            sound.Motor_Play();

            // camera must be initialized
            Camera.View = Matrix.Invert(rotMatrix * transMatrix);

            #region Rotation Control

            rotCtrl = new StarShipRotation(input, Screen.PlayerIndex)
            {
                // yaw
                MaxYawAngle = 30.0f,
                PlayYawTime = TimeSpan.FromMilliseconds(500),
                IdleYawTime = TimeSpan.FromMilliseconds(1000),

                // pitch
                MaxPitchAngle = 7.5f,
                PlayPitchTime = TimeSpan.FromMilliseconds(500),
                IdlePitchTime = TimeSpan.FromMilliseconds(1000),

                // roll
                MaxRollAngle = 90.0f,
                MaxRollYawAngle = 30.0f,
                PlayRollTime = TimeSpan.FromMilliseconds(1000),
                IdleRollTime = TimeSpan.FromMilliseconds(1000),

                // rocking
                RockRollAngle = 5.0f,
                RockRollTime = TimeSpan.FromMilliseconds(1000),
            };

            #endregion

            #region Position Control

            posCtrl = new StarShipTranslation(input, Screen.PlayerIndex)
            {
                MinLongitudinalVelocity = 5.0f,
                MaxLongitudinalVelocity = 35.0f,
                VerticalVelocity = 3.0f,
                LateralVelocity = 3.0f
            };

            #endregion

            #region Velocity Control

            velCtrl = new StarShipVelocity(input, Screen.PlayerIndex)
            {
                IdleTime = TimeSpan.FromMilliseconds(1000),
                BoostTime = TimeSpan.FromMilliseconds(1000),
                BrakeTime = TimeSpan.FromMilliseconds(1000),
                BoostDuration = TimeSpan.FromMilliseconds(30000),
                FuelDurationIdle = TimeSpan.FromMilliseconds(60000),
                FuelDurationBoost = TimeSpan.FromMilliseconds(30000),
            };

            #endregion
        }

        #endregion

        #region Update_Physics

        private void Update_Physics(GameTime gameTime)
        {
            // velocity
            Velocity = velCtrl.ComputeShipVelocity(gameTime);
            sound.Motor_Pitch(Velocity);

            // camera transform
            rotMatrix = rotCtrl.ComputeShipRotation(gameTime);
            transMatrix = posCtrl.ComputeShipTranslation(gameTime, rotMatrix.Forward, Velocity);
            Camera.View = Matrix.Invert(rotMatrix * transMatrix);

            // bounding spheres
            centerSphere.Center = transMatrix.Translation;
            leftSphere.Center = Vector3.Transform(Vector3.Transform(new Vector3(-15.0f, 0, 0), rotMatrix), transMatrix);
            rightSphere.Center = Vector3.Transform(Vector3.Transform(new Vector3(15.0f, 0, 0), rotMatrix), transMatrix);
            globalSphere.Center = transMatrix.Translation;
        }

        #endregion
    }
}
